package com.file.upload.service.impl;

import com.file.upload.dto.ChunkUploadDTO;
import com.file.upload.dto.FileUploadDTO;
import com.file.upload.dto.FileUploadResultDTO;
import com.file.upload.entity.FileChunk;
import com.file.upload.entity.FileInfo;
import com.file.upload.repository.FileChunkRepository;
import com.file.upload.repository.FileInfoRepository;
import com.file.upload.service.FileUploadService;
import com.file.upload.util.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author lhj
 * @Date 2025/4/23 21:27
 */
@Slf4j
@Service
public class FileUploadServiceImpl implements FileUploadService {

    @Value("${file.upload.chunk-dir}")
    private String chunkFileDir;

    @Value("${file.upload.dir}")
    private String uploadFileDir;

    private final FileInfoRepository fileInfoRepository;
    private final FileChunkRepository fileChunkRepository;

    public FileUploadServiceImpl(FileInfoRepository fileInfoRepository, FileChunkRepository fileChunkRepository) {
        this.fileInfoRepository = fileInfoRepository;
        this.fileChunkRepository = fileChunkRepository;
    }

    @Override
    @Transactional
    public FileUploadResultDTO initFileUpload(FileUploadDTO fileUploadDTO) {
        FileUploadResultDTO resultDTO = new FileUploadResultDTO();

        // 检查文件是否已存在（秒传）
        Optional<FileInfo> existingFile = fileInfoRepository.findByFileMd5(fileUploadDTO.getFileMd5());
        if (existingFile.isPresent() && existingFile.get().getFileStatus() == 1) {
            // 文件已存在且已上传完成，可以秒传
            resultDTO.setFileId(existingFile.get().getFileId());
            resultDTO.setSkipUpload(true);
            return resultDTO;
        }

        // 生成文件ID
        String fileId = UUID.randomUUID().toString().replace("-", "");
        resultDTO.setFileId(fileId);
        resultDTO.setSkipUpload(false);

        // 检查是否有未完成的上传任务
        Optional<FileInfo> unfinishedFile = fileInfoRepository.findByFileMd5(fileUploadDTO.getFileMd5());
        if (unfinishedFile.isPresent()) {
            // 使用已有的fileId
            fileId = unfinishedFile.get().getFileId();
            resultDTO.setFileId(fileId);

            // 获取已上传的分片列表
            List<FileChunk> uploadedChunks = fileChunkRepository.findByFileIdAndChunkStatus(fileId, 1);
            List<Integer> uploadedChunkNumbers = uploadedChunks.stream()
                    .map(FileChunk::getChunkNumber)
                    .collect(Collectors.toList());
            resultDTO.setUploadedChunks(uploadedChunkNumbers);

            return resultDTO;
        }

        // 创建新的文件记录
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileId(fileId);
        fileInfo.setFileName(fileUploadDTO.getFileName());
        // 文件路径待合并所有分片后在设置
        fileInfo.setFilePath("");  
        fileInfo.setFileSize(fileUploadDTO.getFileSize());
        fileInfo.setChunkSize(fileUploadDTO.getChunkSize());
        fileInfo.setTotalChunks(fileUploadDTO.getTotalChunks());
        fileInfo.setFileMd5(fileUploadDTO.getFileMd5());
        // 上传中状态，0表示上传中
        fileInfo.setFileStatus(0); 
        fileInfo.setCreateTime(LocalDateTime.now());
        fileInfo.setUpdateTime(LocalDateTime.now());

        fileInfoRepository.save(fileInfo);

        // 创建分片目录
        String chunkDirPath = chunkFileDir + File.separator + fileId;
        File chunkDir = new File(chunkDirPath);
        if (!chunkDir.exists()) {
            chunkDir.mkdirs();
        }

        resultDTO.setUploadedChunks(new ArrayList<>());
        return resultDTO;
    }

    @Override
    @Transactional
    public boolean uploadChunk(ChunkUploadDTO chunkUploadDTO) {
        String fileId = chunkUploadDTO.getFileId();
        Integer chunkNumber = chunkUploadDTO.getChunkNumber();

        // 检查文件是否存在
        Optional<FileInfo> fileInfoOpt = fileInfoRepository.findByFileId(fileId);
        if (!fileInfoOpt.isPresent()) {
            log.error("文件不存在: {}", fileId);
            return false;
        }

        // 检查分片是否已上传
        Optional<FileChunk> existingChunk = fileChunkRepository.findByFileIdAndChunkNumber(fileId, chunkNumber);
        if (existingChunk.isPresent() && existingChunk.get().getChunkStatus() == 1) {
            log.info("分片已上传: fileId={}, chunkNumber={}", fileId, chunkNumber);
            return true;
        }
        
        // 保存分片文件
        MultipartFile file = chunkUploadDTO.getFile();
        String chunkDirPath = chunkFileDir + File.separator + fileId;
        String chunkFilePath = chunkDirPath + File.separator + file.getOriginalFilename();

        try {
            File chunkFile = new File(chunkFilePath);
            chunkUploadDTO.getFile().transferTo(chunkFile);

            // 计算分片MD5
            String chunkMd5 = FileUtils.calculateMd5(chunkFile);

            // 保存或更新分片信息
            FileChunk fileChunk;
            if (existingChunk.isPresent()) {
                fileChunk = existingChunk.get();
            } else {
                fileChunk = new FileChunk();
                fileChunk.setFileId(fileId);
                fileChunk.setChunkNumber(chunkNumber);
                fileChunk.setCreateTime(LocalDateTime.now());
            }

            fileChunk.setChunkSize(chunkFile.length());
            fileChunk.setChunkPath(chunkFilePath);
            fileChunk.setChunkMd5(chunkMd5);
            fileChunk.setChunkStatus(1);  // 已完成
            fileChunk.setUpdateTime(LocalDateTime.now());

            fileChunkRepository.save(fileChunk);

            return true;
        } catch (IOException e) {
            log.error("保存分片失败: fileId={}, chunkNumber={}", fileId, chunkNumber, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean mergeChunks(String fileId) {
        // 获取文件信息
        Optional<FileInfo> fileInfoOpt = fileInfoRepository.findByFileId(fileId);
        if (!fileInfoOpt.isPresent()) {
            log.error("文件不存在: {}", fileId);
            return false;
        }

        FileInfo fileInfo = fileInfoOpt.get();

        // 检查所有分片是否都已上传
        List<FileChunk> chunks = fileChunkRepository.findByFileId(fileId);
        if (chunks.size() != fileInfo.getTotalChunks()) {
            log.error("分片数量不匹配: expected={}, actual={}", fileInfo.getTotalChunks(), chunks.size());
            return false;
        }

        // 检查每个分片的状态
        for (FileChunk chunk : chunks) {
            if (chunk.getChunkStatus() != 1) {
                log.error("分片未完成: fileId={}, chunkNumber={}", fileId, chunk.getChunkNumber());
                return false;
            }
        }

        // 创建目标文件目录
        File uploadDir = new File(uploadFileDir);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }

        // 合并文件
        String targetFilePath = uploadFileDir + File.separator + fileInfo.getFileName();
        try {
            FileUtils.mergeFiles(chunks, targetFilePath);

            // 验证合并后的文件MD5
            File targetFile = new File(targetFilePath);
            String fileMd5 = FileUtils.calculateMd5(targetFile);

            if (!fileMd5.equals(fileInfo.getFileMd5())) {
                log.error("文件MD5不匹配: expected={}, actual={}", fileInfo.getFileMd5(), fileMd5);
                // 删除合并的文件
                targetFile.delete();
                return false;
            }

            // 更新文件信息
            fileInfo.setFilePath(targetFilePath);
            // 已完成
            fileInfo.setFileStatus(1);  
            fileInfo.setUpdateTime(LocalDateTime.now());
            fileInfoRepository.save(fileInfo);

            // 清理分片文件
            String chunkDirPath = chunkFileDir + File.separator + fileId;
            FileUtils.deleteDirectory(new File(chunkDirPath));

            return true;
        } catch (IOException e) {
            log.error("合并文件失败: fileId={}", fileId, e);
            return false;
        }
    }

    @Override
    public FileUploadResultDTO checkFileUploadStatus(String fileId) {
        FileUploadResultDTO resultDTO = new FileUploadResultDTO();
        resultDTO.setFileId(fileId);

        // 获取文件信息
        Optional<FileInfo> fileInfoOpt = fileInfoRepository.findByFileId(fileId);
        if (!fileInfoOpt.isPresent()) {
            resultDTO.setSkipUpload(false);
            resultDTO.setUploadedChunks(new ArrayList<>());
            return resultDTO;
        }

        FileInfo fileInfo = fileInfoOpt.get();

        // 如果文件已完成，可以秒传
        if (fileInfo.getFileStatus() == 1) {
            resultDTO.setSkipUpload(true);
            return resultDTO;
        }

        // 获取已上传的分片
        List<FileChunk> uploadedChunks = fileChunkRepository.findByFileIdAndChunkStatus(fileId, 1);
        List<Integer> uploadedChunkNumbers = uploadedChunks.stream()
                .map(FileChunk::getChunkNumber)
                .collect(Collectors.toList());

        resultDTO.setSkipUpload(false);
        resultDTO.setUploadedChunks(uploadedChunkNumbers);

        return resultDTO;
    }
}
